import logging

from dc_shop.core.models.dto import OrderDTO
from dc_shop.core.models.dto.order import OrderWithDetailsDTO
from dc_shop.core.models.enums.order import DeliveryMethod, OrderStatus
from dc_shop.database.converters.order import convert_db_model_to_order_with_details_dto
from dc_shop.database.models import Order, OrderLine, Stock, User
from dc_shop.database.uow import SQLAlchemyUoW

logger = logging.getLogger(__name__)


class OrderService:
    def __init__(self, uow: SQLAlchemyUoW):
        self._uow = uow

    async def get_order_by_id(self, order_id: int) -> OrderWithDetailsDTO:
        """Получаем заказ по id"""

        return await self._uow.order_repo.get_order_by_id(order_id)

    async def get_user_orders(self, user_tg_id: int) -> list[OrderWithDetailsDTO]:
        """Получаем заказы пользователя"""

        return await self._uow.order_repo.get_user_orders(user_tg_id)

    async def get_orders_for_confirmation(self) -> list[OrderWithDetailsDTO]:
        """Получаем заказы с неподтвержденной оплатой"""

        return await self._uow.order_repo.get_orders_for_confirmation()

    async def get_orders_for_shipping(self) -> list[OrderWithDetailsDTO]:
        """Получаем заказы для отгрузки"""

        return await self._uow.order_repo.get_orders_for_shipping()

    async def get_expired_payment_orders(self) -> list[OrderWithDetailsDTO]:
        """Получаем заказы с более чем часом в статусе payment_canceled"""

        return await self._uow.order_repo.get_expired_payment_orders()

    async def get_completed_orders(self) -> list[OrderDTO]:
        """Получаем заказы с более чем 3 часами в статусе in transit"""

        return await self._uow.order_repo.get_completed_orders()

    async def get_order_with_promocode(self, user_id: int, promocode_id: int) -> OrderDTO:
        """Получаем заказ с промокодом"""

        return await self._uow.order_repo.get_order_with_promocode(user_id, promocode_id)

    async def create_order(
        self,
        summ: float,
        user_tg_id: int,
        shop_id: int,
        payment_method_id: int,
        order_lines: list[OrderLine],
        comment: str | None = None,
        promocode_id: int | None = None,
        delivery_method: DeliveryMethod = DeliveryMethod.courier,
        order_status: OrderStatus = OrderStatus.created,
    ) -> OrderWithDetailsDTO:
        """
        Создаем заказ
        @param summ: Сумма заказа
        @param user_tg_id: Telegram id пользователя
        @param shop_id: Id магазина
        @param payment_method_id: Id метода оплаты
        @param order_lines: Список купленных товаров
        @param comment: Комментарий
        @param promocode_id: Id промокода
        @param delivery_method: Метод доставки заказа
        @param order_status: Статус заказа
        @return:
        """

        user: User = await self._uow.user_repo.get_user_by_tg_id(user_tg_id)
        shipping_address = None
        if delivery_method == DeliveryMethod.courier:
            shipping_address = user.address
        order = Order(
            summ=summ,
            status=order_status,
            order_lines=order_lines,
            user_id=user.id,
            delivery_method=delivery_method,
            shipping_address=shipping_address,
            user_phone=user.phone,
            shop_id=shop_id,
            payment_method_id=payment_method_id,
            comment=comment,
            promocode_id=promocode_id,
        )
        # Уменьшаем остатки по товару в магазине
        for line in order_lines:
            stock: Stock = await self._uow.product_repo.get_product_stock_in_shop(shop_id, line.product_id)
            # TODO Рейзить ошибку, если число товаров изменилось
            if stock.available_quantity > 0:
                stock.available_quantity -= line.quantity
                await self._uow.product_repo.update_stock(stock)
        try:
            await self._uow.order_repo.create_order(order)
        except Exception as err:
            await self._uow.rollback()
            raise err

        await self._uow.commit()

        logger.info(f"Order was created: {order.id}")

        order_dto = convert_db_model_to_order_with_details_dto(order, False, True)

        return order_dto

    async def update_order_status(self, order_id: int, order_status: OrderStatus) -> None:
        """Обновляем статус заказа"""

        order: Order = await self._uow.order_repo.get_order_by_id(order_id, as_dto=False)
        order.status = order_status
        await self._uow.order_repo.update_order(order)

        await self._uow.commit()

        logger.info(f"Order {order_id} status was updated. New status: {order_status}")

    async def update_order_summ(self, order_id: int, summ: float) -> None:
        """Обновляем сумму заказа"""

        order: Order = await self._uow.order_repo.get_order_by_id(order_id, as_dto=False)
        order.summ = summ
        await self._uow.order_repo.update_order(order)

        await self._uow.commit()

        logger.info(f"Order {order_id} summ was updated. New summ: {summ}")

    async def update_order_invoice_path(self, order_id: int, invoice_filename: str) -> None:
        """Обновляем путь к скриншоту об оплате"""

        order: Order = await self._uow.order_repo.get_order_by_id(order_id, as_dto=False)
        order.status = OrderStatus.payment_not_confirmed
        order.invoice_screenshot_path = invoice_filename

        await self._uow.order_repo.update_order(order)

        await self._uow.commit()

        logger.info(f"Order {order_id} invoice screenshot path was updated")

    async def update_order_payment_method(self, order_id: int, payment_method_id: int) -> None:
        """Обновляем способ оплаты заказа"""

        order: Order = await self._uow.order_repo.get_order_by_id(order_id, as_dto=False)
        order.payment_method_id = payment_method_id
        order.status = OrderStatus.created

        await self._uow.order_repo.update_order(order)

        await self._uow.commit()

        logger.info(f"Order {order_id} payment method was updated")

    async def update_order_tracking_link(self, order_id: int, tracking_link: str) -> None:
        """Обновляем ссылку на отслеживание заказа"""

        order: Order = await self._uow.order_repo.get_order_by_id(order_id, as_dto=False)
        order.tracking_link = tracking_link
        order.status = OrderStatus.in_transit

        await self._uow.order_repo.update_order(order)

        await self._uow.commit()

        logger.info(f"Order {order_id} tracking link was updated")
